package panda.planner.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import panda.query.condition.AttributeName;
import panda.query.condition.ConditionExtract;
import panda.query.condition.DecomposeResult;
import panda.query.condition.Sentence;
import panda.query.condition.ValueExpression;
import panda.query.struct.Attribute;
import panda.query.struct.SemAttribute;
import panda.record.Schema;
import panda.record.TableToken;
import panda.server.Panda;
import panda.syntax.AttributeValue;
import panda.syntax.ColumnName;
import panda.syntax.DeleteStm;
import panda.syntax.SelectStm;
import panda.syntax.SetValue;
import panda.syntax.Statement;
import panda.syntax.TblRef;
import panda.syntax.UpdateStm;
import panda.transaction.Transaction;

/**
 * This class contains functions that formalize different expressions
 * @author Tian Yuan
 *
 */
public class ConditionFormalizer {

	//Formalize SELECT statement

	/**
	 * 
	 */
	public static void formalizeSelect(SelectStm stm, Map<String, TableToken> map, Map<TableToken, Statement> tt2ss, Transaction tx) {
		Set<TableToken> tn = new HashSet<TableToken>();
		Sentence cond;
		DecomposeResult res;
		Map<TableToken, Statement> prev = new HashMap<TableToken, Statement>(tt2ss);



		for (TblRef tr : stm.getTables()) {
			TableToken tt = Panda.getMetaInfoManager().getTableToken(tr.getTableName(), tx);
			tr.setToken(tt);
			tn.add(tt);
//			tt2ss.put(tt, stm);
			if (tr.getAlias() != null)
				map.put(tr.getAlias(), tt);
			else
				map.put(tr.getTableName(), tt);
		}
		
		for (TblRef rf : stm.getTables())
			tt2ss.put(rf.getToken(), stm);

		cond = stm.getWhereCondition();
		if (cond != null) {
			res = ConditionExtract.decompose(cond);


			for (AttributeName a : res.attrSet) {
				SemAttributeResult sr = getSemanticAttribute(stm, ((AttributeValue)a.getAttribute()).columnName, ((AttributeValue)a.getAttribute()).func, map);
				a.instantiate(sr.semAttr);
				if (prev.get(sr.token) != null) {
					prev.get(sr.token).subRefered.add(sr.semAttr);
					stm.subReferring.add(a);
				}
			}
			
			for (SelectStm s : res.subSet)
				formalizeSelect(s, map, tt2ss, tx);
		}

		cond = stm.getHavingCondition();
		if (cond != null) {
			res = ConditionExtract.decompose(cond);

			for (AttributeName a : res.attrSet) {
				SemAttributeResult sr = getSemanticAttribute(stm, ((AttributeValue)a.getAttribute()).columnName, ((AttributeValue)a.getAttribute()).func, map);
				
				a.instantiate(sr.semAttr);
				if (prev.get(sr.token) != null) {
					prev.get(sr.token).subRefered.add(sr.semAttr);
					stm.subReferring.add(a);
				}
				
				
				if (((SemAttribute)a.getAttribute()).getBindAttribute() != null)
					stm.fixedAttr.add(((SemAttribute)a.getAttribute()).getBindAttribute());
				else
					stm.fixedAttr.add(a.getAttribute());
			}
			
			for (SelectStm s : res.subSet) formalizeSelect(s, map, tt2ss, tx);
		}

		if (stm.attrs == null) {
			stm.star = true;
			
			stm.attrs = new ArrayList<ValueExpression>();
			for (TblRef t : stm.getTables()) {
				
				Map<Integer, Attribute> attrMap = new HashMap<Integer, Attribute>();
				
				for (Attribute a : t.getToken().getSchema().getAllAttributes())
					attrMap.put(t.getToken().getSchema().IDof(a.getAttributeName()), a);
				
				for (int i = 1; i <= attrMap.size(); i++) {
					Attribute a = attrMap.get(i);
					
					stm.attrs.add(new AttributeName(a));
					stm.selectAttr.add(a);
				}
			}
		}
		else
			for (ValueExpression i : stm.attrs) {
				res = ConditionExtract.decompose(i);

				for (AttributeName a : res.attrSet) { 
					SemAttributeResult sr = getSemanticAttribute(stm, ((AttributeValue)a.getAttribute()).columnName, ((AttributeValue)a.getAttribute()).func, map); 
					a.instantiate(sr.semAttr);
					if (prev.get(sr.token) != null) {
						prev.get(sr.token).subRefered.add(sr.semAttr);
						stm.subReferring.add(a);
					}

					if (((SemAttribute)a.getAttribute()).getBindAttribute() != null)
						stm.selectAttr.add(((SemAttribute)a.getAttribute()).getBindAttribute());
					else
						stm.selectAttr.add(a.getAttribute());
				}
				
				for (SelectStm s : res.subSet) formalizeSelect(s, map, tt2ss, tx);
			}

		if (stm.groupbyCol != null)
			for (ColumnName c : stm.groupbyCol)
				stm.groupbySem.add(getSemanticAttribute(stm, c, 0, map).semAttr);

		if (stm.orderbyCol != null)
			for (ColumnName c : stm.orderbyCol)
				stm.orderbySem.add(getSemanticAttribute(stm, c, 0, map).semAttr);
		

	}

	/**
	 * 
	 * @param stm
	 * @param av
	 * @param func
	 * @param map
	 * @return
	 */
	private static SemAttributeResult getSemanticAttribute(SelectStm stm, ColumnName av, int func, Map<String, TableToken> map) {
		if (av.tableName == null) {
			//Find local attributes
			for (TblRef tr : stm.getTables()) {
				SemAttribute a;
				if ((a = (SemAttribute)tr.getToken().getSchema().getAttributeByName(av.columnName)) != null) {
//					if (tt2ss.get(a) != null) tt2ss.get(a).subRefAttr.add(a);
					if (func == 0)
						return new SemAttributeResult(tr.getToken(), a);
					else
						return new SemAttributeResult(tr.getToken(), stm.findAggregate(a, func));
				}
			}
			
			//Find other attributes
			for (TableToken tt : map.values()) {
				SemAttribute a;
				if ((a = (SemAttribute)tt.getSchema().getAttributeByName(av.columnName)) != null) {
//					if (tt2ss.get(a) != null) tt2ss.get(a).subRefAttr.add(a);
					if (func == 0)
						return new SemAttributeResult(tt, a);
					else
						return new SemAttributeResult(tt, stm.findAggregate(a, func));
				}
			}
			return null;
		}
		else {
			TableToken tt = map.get(av.tableName);
			SemAttribute a = (SemAttribute)tt.getSchema().getAttributeByName(av.columnName);
			
//			if (tt2ss.get(a) != null) tt2ss.get(tt).subRefAttr.add(a);
			
			if (func == 0)
				return new SemAttributeResult(tt, a);
			else
				return new SemAttributeResult(tt, stm.findAggregate(a, func));
		}
	}

	//Formalize UPDATE statement

	/**
	 * 
	 * @param stm
	 */
	public static void formalizeUpdate(UpdateStm stm, Transaction tx) {
		stm.token = Panda.getMetaInfoManager().getTableToken(stm.tableName, tx);
		Schema sch = stm.token.getSchema();

		DecomposeResult dr = ConditionExtract.decompose(stm.whereCondition);
		Map<String, TableToken> map = new HashMap<String, TableToken>();
		Map<TableToken, Statement> tt2ss = new HashMap<TableToken, Statement>();
		map.put(stm.tableName, stm.token);

		for (SelectStm s : dr.subSet) formalizeSelect(s, map, tt2ss, tx);

		for (AttributeName a : dr.attrSet)
			a.instantiate((SemAttribute)sch.getAttributeByName(a.getAttribute().getAttributeName()));
		
		for (SetValue sv : stm.setValueList) {
			dr = ConditionExtract.decompose(sv.value);
			
			for (SelectStm s : dr.subSet) formalizeSelect(s, map, tt2ss, tx);
			
			for (AttributeName a : dr.attrSet)
				a.instantiate((SemAttribute)sch.getAttributeByName(a.getAttribute().getAttributeName()));
		}

	}

	//Formalize DELETE statement

	public static void formalizeDelete(DeleteStm stm, Transaction tx) {
		stm.token = Panda.getMetaInfoManager().getTableToken(stm.tableName, tx);
		Schema sch = stm.token.getSchema();

		DecomposeResult dr = ConditionExtract.decompose(stm.whereCondition);
		Map<String, TableToken> map = new HashMap<String, TableToken>();
		Map<TableToken, Statement> tt2ss = new HashMap<TableToken, Statement>();
		map.put(stm.tableName, stm.token);

		for (SelectStm s : dr.subSet) formalizeSelect(s, map, tt2ss, tx);

		for (AttributeName a : dr.attrSet)
			a.instantiate((SemAttribute)sch.getAttributeByName(a.getAttribute().getAttributeName()));
	}

}
